<?php

global $bolGNoCargarSpreadsheetExcelWriter;
error_Reporting(E_ALL);
if(isset($bolGNoCargarSpreadsheetExcelWriter) && $bolGNoCargarSpreadsheetExcelWriter) {
  ;
} else {
  include_once("Spreadsheet/Excel/Writer.php");
}

require_once(BaseConfig::LoadClass('reporte_pdf.class.php'));

class Reporte {
  /************** VARIABLES NECESARIAS **************/
  
  /**
   * Tipo de conexion que se usará:
   * 
   * 0 => MySQL
   * 1 => PostgreSQL
   * 
   * @var type 
   */
  var $intTipoConexion;

  /**
   * Nombre del reporte
   * @var String
   */
  var $strNombreReporte;

  /**
   * Si se usará un tr de distinto color para cada columna
   * @var boolean
   */
  var $bolAlternarConClase;

  /**
   * Información del layout
   * 
   * Este arreglo contiene la información de las columnas que aparecerán en el
   * reporte.
   *
   * Cada columna está compuesta de un identificado (nombre corto) que relaciona
   * cada columna con los datos en el arreglo de resultados.
   *
   * Existen nombres de columna especiales que indican ciertas propiedades de las
   * celdas, y que se calculan automaticamente, los cuales son:
   *
   * xConsReg  => Consecutivo de cada registro
   * xConsGrup => Si se está agrupando, este registro contendrá el consecutivo del
   *              grupo en el que el dato se encuentra
   * 
   * xURL_{NombreColumna} => (Solo en la clase estadistica) Se pone una URL la 
   *              cual se abrirá al darle click al dato. Esta URL NO se genera
   *              automaticamente, y lo más conveniente es ponerla en 
   *              "__ArreglarDatosPrevioGrafica" o en "ManejarReporte"
   *
   * A su vez, cada
   * identificador contiene dos partes que le indican al sistema de que manera se
   * comprtará dicha columna:
   *
   *
   * Tipo: Es el tipo de datos que contiene la columna, y puede ser:
   *   S  => Cadena de caracteres
   *   C  => Cadena de caracteres, pero cuando esta columna sea parte de un
   *         subtotal o de un total, en esta columna se pondrá la leyenda
   *         "Subtotal" o "Total". Si se cuenta con un digito despues de la C, 
   *         ese será el numero de posiciones (colspan) que tendrá esa columna
   *         al presentar los subtotales.
   *   T  => Fecha
   *   I  => Número entero
   *   Ix => Número entero
   *   D  => Número decimal (a dos digitos)
   *   Dx => Número decimal (a dos digitos)
   *   F  => Número decimal (a dos digitos, con separador de miles y el simbolo $)
   *         (Moneda)
   *   Fx => Número decimal (a dos digitos, con separador de miles y el simbolo $)
   *         (Moneda)
   *
   * Por default el tipo será C la primera vez, S en las siguientes.
   * 
   * Los tipos que contienen una x minuscula al final se diferencian de los que no
   * en que esos datos presentarán dos guiones cuando el valor del dato sea cero,
   * y que serán tomados en cuenta para los subtotales y el total (estas columnas
   * serán las que se sumarán)
   *
   *
   * Nombre: Será el nombre en la columna que será desplegado como cabecera (a lo
   * que se refiere dicho dato)
   *
   * (...That's what SHE said! :P  )
   * 
   * Por ejemplo:
   * $arrInfoReporte["indice"]["Tipo"  ] = "C";
   * $arrInfoReporte["indice"]["Nombre"] = "Nombre del Indice";
   * 
   * @var array
   */
  var $arrInfoReporte  ;

  /**
   * Información de las cabeceras
   *
   * Si la cabecera del reporte (Esto es, las columnas de hasta arriba) son todas
   * independientes (esto es, no hay conjuntos de datos agrupados), no hay
   * necesidad de armar el arreglo $arrDifHeader (Esto es, puede ir vacio o
   * incluso no definido).
   *
   * Sin embargo, si uno se encuentra con la necesidad de agrupar dos o más
   * cabeceras con un encabezado, se puede indicar dicha agrupación con este
   * arreglo, el cual tiene el siguiente formato:
   *
   * $arrDifHeader[] = Array(Componentes de la cabecera)
   *
   * Si el arreglo indicado solo contiene un elemento, dicho elemento será tomado
   * como que se refiere al indice de una entrada en $arrInfoReporte, y se
   * presentará sin ser agrupado por alguna otra cabecera. Pero si el arreglo
   * cuenta con más de un elemento (Esto es, "Componentes de la cabecera" es más
   * de un elemento separado por comas), el primer elemento será considerado el
   * texto de la cabecera que agrupa, y los elementos restantes los indices de
   * entradas que serán agrupadas por dicha cabecera (Esto es, el elemento
   * cabecera tendrá un colspan de tantos elementos como contenga el arreglo menos
   * uno).
   * 
   * @var array
   */
  var $arrDifHeader   ;

  /**
   * Arreglo que contiene información de colores diferentes para las cabeceras.
   * Está muy ligado con $arrDifHeader, en que cada indice de ese arreglo está
   * reflejado en este arreglo diciendo la combinación de colores primarios
   * que se va a usar. Se especifica cada uno de los 3 colores primarios. Si no
   * se especifica, se piensa que es el color normal.
   *
   * $arrDifHeaderCol[0] = array(red, green, blue);
   *
   * @var array
   */
  var $arrDifHeaderCol;

  /**
   * Plantilla base del query
   *
   * Esta variable contiene la base para el query. Otras opciones serán agregadas
   * despues del query con HAVING, sin embargo no es necesario que este query
   * contenga (o que no contenga) algún HAVING. Cada columna se relaciona con el
   * indice de $arrInfoReporte, por lo que si dichos indices no son nombres de
   * columnas, se necesitará hacer alias en el query.
   * 
   * @var String
   */
  var $strConsultaIni  ;
  /************** VARIABLES NECESARIAS **************/

  /**
   * Indica cual es la columna en la cual se agruparán los datos.
   * 
   * @var String
   */
  var $strOpAgrupar;

  /**
   * Cadena que contendrá lo que dice "SUBTOTAL"
   * @var String
   */
  var $strLeyendaSubTotal;

  /**
   * Indica si la columna de subtotales se mostrara ANTES del contenido de dicho
   * subtotal.
   *
   * @var boolean
   */
  var $bolSubtotalAntes;

  /**
   * Variable que contiene el workbook de excel para cuando se generan reportes
   * en excel.
   * 
   * @var Spreadsheet_Excel_Writer
   */
  var $rsWorkBook;

  function Reporte() {

  }

  /**
   * Función que inicializa el reporte. Para más información de cada uno de los
   * parametros, ver directamente la información de cada parametro en la ayuda.
   *
   * @param String  $strNombreReporte Nombre del reporte
   * @param array   $arrInfoReporte   Información del reporte
   * @param String  $strConsultaIni   Consulta inicial
   * @param String  $strOpAgrupar     Si se agrupa por algun campo
   * @param array   $arrDifHeader     Si existe una agrupación de header diferente
   * @param array   $arrDifHeaderCol  Colores para cada uno de las agrupaciones
   * @param boolean $bolSubtotalAntes Si se pondran los subtotales ANTES de el contenido de lo que es el subtotal
   */
  function Inicializar($strNombreReporte, $arrInfoReporte, $strConsultaIni, $strOpAgrupar = "", $arrDifHeader = "", $arrDifHeaderCol = "", $bolSubtotalAntes = false) {
    $this->strNombreReporte = $strNombreReporte;
    $this->arrInfoReporte   = $arrInfoReporte  ;
    $this->arrDifHeader     = $arrDifHeader    ;
    $this->arrDifHeaderCol  = $arrDifHeaderCol ;
    $this->strConsultaIni   = $strConsultaIni  ;
    $this->strOpAgrupar     = $strOpAgrupar    ;
    $this->bolSubtotalAntes = $bolSubtotalAntes;

    if(!$this->strLeyendaSubTotal) {
      $this->strLeyendaSubTotal = "Subtotal";
    }
    $this->rsEstiloDefault     = "AAA";
    $this->intTipoConexion     = 0;
    $this->bolAlternarConClase = false;

    $this->__ArreglarInfoReporte();
  }

  /**
   * Función que maneja los datos del reporte.
   *
   * Se puede sobrecargar en una clase derivada para lograr que el reporte sea
   * manejado diferente.
   *
   * @param array $arrReporte Arreglo con los datos preprocesados en el reporte.
   * @return array Arreglo con los datos que el reporte tendrá.
   */
  function ManejarReporte($arrReporte) {
    return $arrReporte;
  }

  /**
   * Función que maneja cada linea del reporte. Se envia DESPUES de haber sido
   * manejada por manejarReporte
   *
   * @param Array $arrLinea linea que se manejará
   */
  function ManejarLinea($arrLinea) {
    return $arrLinea;
  }
  
  function __GetConectaObject() {
    if($this->intTipoConexion == 0) {
      $oConecta = new Conecta();
    } else if($this->intTipoConexion == 1) {
      $oConecta = new ConectaP();
    }
    
    return $oConecta;
  }
  
  function __ArreglarDatosPrevioMuestreo($bolIncluirColumnaTotales = true, $bolEnExcel = false, $arrFiltros = "", $bolLlamarArrangeArray = true) {
    $oConecta = $this->__GetConectaObject();
    
    if(!$arrFiltros) {
      $arrFiltros = array();
    }
    
    $strConsulta = $this->__GenerarQuery($arrFiltros);
    $oConecta->Consulta($strConsulta);
    $arrReporte = $oConecta->GetCompleteRes();
    $arrReporte = $this->ManejarReporte($arrReporte);
    if($bolLlamarArrangeArray) {
      $arrReporte = $this->__ArrangeArray($arrReporte, $bolIncluirColumnaTotales);
    }
    
    return $arrReporte;
  }
  
  function __ArreglarDatosPrevioPDF($arrReporte) {
    return $arrReporte;
  }

  /**
   * Función que genera el reporte.
   *
   * Si se le pasa el parametro $bolEnExcel, se mandará al navegador el archivo
   * EXCEL del reporte para ser descargado, de otra manera se regresará una
   * cadena con la tabla HTML del reporte.
   *
   * @param boolean $bolIncluirColumnaTotales Si se incluirán las columnas de totales y subtotales
   * @param boolean $bolEnExcel               Si el reporte se requiere en excel.
   * @param array   $arrFiltros               ** No usado por el momento **
   * @return String Reporte en HTML, o nada si se pidió el reporte en excel.
   */
  function DoReporte($bolIncluirColumnaTotales = true, $bolEnExcel = false, $arrFiltros = "") {
    $arrReporte = $this->__ArreglarDatosPrevioMuestreo($bolIncluirColumnaTotales, $bolEnExcel, $arrFiltros);

    if($bolEnExcel) {
      error_reporting(0);
      return $this->__DoReportePear($arrReporte);
    } else {
      return $this->__DoReporteHTML($arrReporte);
    }
  }
  
  function DoReporteHTML($bolIncluirColumnaTotales = true, $arrFiltros = "") {
    $arrReporte = $this->__ArreglarDatosPrevioMuestreo($bolIncluirColumnaTotales, false, $arrFiltros);
    return $this->__DoReporteHTML($arrReporte);
  }

  function DoReporteExcel($bolIncluirColumnaTotales = true, $arrFiltros = "") {
    $arrReporte = $this->__ArreglarDatosPrevioMuestreo($bolIncluirColumnaTotales, true, $arrFiltros);
    return $this->__DoReportePear($arrReporte);
  }

  function DoReportePDF($bolIncluirColumnaTotales = true, $arrFiltros = "") {
    $arrReporte = $this->__ArreglarDatosPrevioMuestreo($bolIncluirColumnaTotales, false, $arrFiltros);
    $arrReporte = $this->__ArreglarDatosPrevioPDF($arrReporte);
    return $this->__DoReporteTCPDF($arrReporte);
  }

  function __ArreglarInfoReporte() {
    $bolCPuesta = false;
    // Checar si existe una C en los tipos que ya se han ingresado
    if(is_array($this->arrInfoReporte)) {
      foreach($this->arrInfoReporte as $strIndice => $arrReporte) {
        if(isset($arrReporte["Tipo"]) && ($arrReporte["Tipo"]{0} == "C")) {
          $bolCPuesta = true;
        }
      }

      foreach($this->arrInfoReporte as $strIndice => $arrReporte) {
        if(!isset($arrReporte["Tipo"])) {
          if($bolCPuesta) {
            $this->arrInfoReporte[$strIndice]["Tipo"] = "S";
          } else {
            $this->arrInfoReporte[$strIndice]["Tipo"] = "C";
            $bolCPuesta = true;
          }
        }
      }
    }
  }

  function __GenerarQuery($arrFiltros) {
    $strHaving = "";

    foreach($arrFiltros as $strNombreFiltro => $arrInfoFiltros) {
      if($strNombreFiltro == "xxExcel"  ) continue;
      if($strNombreFiltro == "xxAgrupar") continue;
      if($strNombreFiltro == "xxFecha"  ) continue;

      foreach($arrInfoFiltros as $arrFiltroI) {
        if(trim($arrFiltroI["va"])) {
          switch($arrFiltroI["op"]) {
            case '=':
              $strHaving .= "$strNombreFiltro  = '" . trim($arrFiltroI["va"]) . "' AND ";
              break;
            case '!':
              $strHaving .= "$strNombreFiltro != '" . trim($arrFiltroI["va"]) . "' AND ";
              break;
            case '>':
              $strHaving .= "$strNombreFiltro >  '" . trim($arrFiltroI["va"]) . "' AND ";
              break;
            case '>=':
              $strHaving .= "$strNombreFiltro >= '" . trim($arrFiltroI["va"]) . "' AND ";
              break;
            case '<':
              $strHaving .= "$strNombreFiltro < '" . trim($arrFiltroI["va"]) . "' AND ";
              break;
            case '<=':
              $strHaving .= "$strNombreFiltro <= '" . trim($arrFiltroI["va"]) . "' AND ";
              break;
            case '%':
              $strHaving .= "$strNombreFiltro LIKE '%" . trim($arrFiltroI["va"]) . "%' AND ";
              break;
          }
        }
      }
    }

    if($strHaving) {
      $strHaving = substr($strHaving, 0, -5);
      if(strpos($this->strConsultaIni, 'HAVING')) {
        $strConsulta = "{$this->strConsultaIni} AND $strHaving";
      } else {
        $strConsulta = "{$this->strConsultaIni} HAVING $strHaving";
      }
    } else {
      $strConsulta = $this->strConsultaIni;
    }


    return $strConsulta;
  }

  function __ArrangeArray($arrReporte, $bolHacerTotales = true) {
    $arrSubT = array();
    $arrRet  = array();
    $arrTot  = array();
    $arrR3   = array();
    if($this->strOpAgrupar && is_array($arrReporte)) {
      foreach($arrReporte as $strKeyCol => $arrRepCol) {
        $arrRet[$arrRepCol[$this->strOpAgrupar]][$strKeyCol] = $arrRepCol;

        if($bolHacerTotales) {
          $arrSubT[$arrRepCol[$this->strOpAgrupar]]["xSubTotx"] = "1";
          $arrTot ["xTotx"] = "1";


          foreach($this->arrInfoReporte as $strNombreCol => $arrInfoCol) {
            if((strlen($arrInfoCol["Tipo"]) > 1) && ($arrInfoCol["Tipo"]{1} == 'x')) {
              if(!isset($arrSubT[$arrRepCol[$this->strOpAgrupar]][$strNombreCol])) {
                $arrSubT[$arrRepCol[$this->strOpAgrupar]][$strNombreCol] = 0;
              }
              $arrSubT[$arrRepCol[$this->strOpAgrupar]][$strNombreCol] += $arrRepCol[$strNombreCol];
            } else {
              if($arrInfoCol["Tipo"]{0} == "C") {
                $arrSubT[$arrRepCol[$this->strOpAgrupar]][$strNombreCol] = "{$this->strLeyendaSubTotal} {$arrRepCol[$this->strOpAgrupar]}";
              } else {
                $arrSubT[$arrRepCol[$this->strOpAgrupar]][$strNombreCol] = "";
              }
            }

            if((strlen($arrInfoCol["Tipo"]) > 1) && ($arrInfoCol["Tipo"]{1} == 'x')) {
              if(!isset($arrTot[$strNombreCol])) {
                $arrTot[$strNombreCol] = 0;
              }
              $arrTot[$strNombreCol] += $arrRepCol[$strNombreCol];
            } else {
              if($arrInfoCol["Tipo"]{0} == "C") {
                $arrTot[$strNombreCol] = "Total";
              } else {
                $arrTot[$strNombreCol] = "";
              }
            }
          }
        }
      }

      $intTotRegistros = 0;
      $intTotGrupos    = 0;
      if(isset($arrRet) && is_array($arrRet)) {
        foreach($arrRet as $strKC => $arrR1) {
          $intTotGrupos++;
          if($bolHacerTotales && $this->bolSubtotalAntes) {
//            $arrSubT[$strKC]["xConsGrup"] = $intTotGrupos;
            $arrR3[] = $arrSubT[$strKC];
          }
          foreach($arrR1 as $arrR2) {
            ++$intTotRegistros;
            $arrR2["xConsReg" ] = $intTotRegistros;
            $arrR2["xConsGrup"] = $intTotGrupos;
            $arrR3[] = $arrR2;
          }
          if($bolHacerTotales && !($this->bolSubtotalAntes)) {
//            $arrSubT[$strKC]["xConsGrup"] = $intTotGrupos;
            $arrR3[] = $arrSubT[$strKC];
          }
        }
      }

      if($bolHacerTotales) {
        $arrR3[] = @$arrTot;
      }
    } else if($bolHacerTotales && is_array($arrReporte)) {
      $arrTot ["xTotx"] = "1";
      foreach($arrReporte as $arrRepCol) {
        foreach($this->arrInfoReporte as $strNombreCol => $arrInfoCol) {
          if((strlen($arrInfoCol["Tipo"]) > 1) && ($arrInfoCol["Tipo"]{1} == 'x')) {
            if(!isset($arrTot[$strNombreCol])) {
              $arrTot[$strNombreCol] = 0;
            }
            $arrTot[$strNombreCol] += $arrRepCol[$strNombreCol];
          } else {
            if($arrInfoCol["Tipo"]{0} == "C") {
              $arrTot[$strNombreCol] = "Total";
            } else {
              $arrTot[$strNombreCol] = "";
            }
          }
        }
      }

      $intTotRegistros = 0;
      foreach($arrReporte as $arrRep) {
        ++$intTotRegistros;
        $arrRep["xConsReg" ] = $intTotRegistros;
        $arrRep["xConsGrup"] = "1";
        $arrR3 [] = $arrRep;
      }

      $arrR3[] = $arrTot;
    } else {
      if(is_array($arrReporte)) {
        $intTotRegistros = 0;
        foreach($arrReporte as $arrRep) {
          ++$intTotRegistros;
          $arrRep["xConsReg" ] = $intTotRegistros;
          $arrRep["xConsGrup"] = "1";
          $arrR3 [] = $arrRep;
        }
      } else {
        $arrR3 = $arrReporte;
      }
    }
    
    foreach($arrR3 as $intIndice => $arrLinea) {
      $arrR3[$intIndice] = $this->ManejarLinea($arrLinea);
    }

    return $arrR3;
  }

  function __CambiarSimbolos($strCadena) {
    return html_entity_decode(str_replace("<br />", " ", $strCadena));
  }

  static function GetHexColor($intRed, $intGreen, $intBlue) {
    $strRed   = strtoupper(dechex($intRed  ));
    $strGreen = strtoupper(dechex($intGreen));
    $strBlue  = strtoupper(dechex($intBlue ));

    if(strlen($strRed  ) < 2) { $strRed   = "0" . $strRed  ; }
    if(strlen($strGreen) < 2) { $strGreen = "0" . $strGreen; }
    if(strlen($strBlue ) < 2) { $strBlue  = "0" . $strBlue ; }

    return "$strRed$strGreen$strBlue";
  }

  function GetHeaderStyle($intIndex) {
    if(!isset($this->arrDifHeaderCol[$intIndex])) {
      return "";
    }

    if(is_array($this->arrDifHeaderCol[$intIndex])) {
      if(count($this->arrDifHeaderCol[$intIndex]) == 3) {
        return "style='background-color:#" . Reporte::GetHexColor($this->arrDifHeaderCol[$intIndex][0], $this->arrDifHeaderCol[$intIndex][1], $this->arrDifHeaderCol[$intIndex][2]) . ";'";
      } else if(count($this->arrDifHeaderCol[$intIndex]) == 6) {
        return "style='background-color:#" . Reporte::GetHexColor($this->arrDifHeaderCol[$intIndex][0], $this->arrDifHeaderCol[$intIndex][1], $this->arrDifHeaderCol[$intIndex][2]) . ";color:#" . Reporte::GetHexColor($this->arrDifHeaderCol[$intIndex][3], $this->arrDifHeaderCol[$intIndex][4], $this->arrDifHeaderCol[$intIndex][5]) . "'";
      } else {
        return "";
      }
    } else {
      if($this->arrDifHeaderCol[$intIndex] == $intIndex) {
        return "";
      } else {
        return $this->GetHeaderStyle($this->arrDifHeaderCol[$intIndex]);
      }
    }

  }

  function IncluirHTMLDespues() {
    return "";
  }
  
  function IncluirHTMLAntes() {
    return "";
  }
  
  function __DoReporteTCPDF($arrReporte) {
    $oReportePDF = new ReportePDF($this, $arrReporte);
    
    return $oReportePDF->DoRender();
  }
  
  function __DoReporteHTML($arrReporte) {
    $strTabla  = "";

    $strTabla .= "<table class='TablaReportes'>";
    
    $strTabla .= $this->IncluirHTMLAntes();

    $strTabla .= "<tr><th colspan='" . count($this->arrInfoReporte) . "'>{$this->strNombreReporte}</th></tr>";

    // Si se pasa el arreglo de arrDifHeader, y este contiene algun elemento cuyo
    // count sea mayor a 1, el header es distinto
    $bolHeaderCreado = false;
    if($this->arrDifHeader != "") {
      $bolColumnasAgrupadas = false;
      foreach($this->arrDifHeader as $arrInfoHeader) {
        if(count($arrInfoHeader) > 1) {
          $bolColumnasAgrupadas = true;
          break;
        }
      }

      if($bolColumnasAgrupadas) {
        $bolHeaderCreado = true;
        $strHeaderRowsTop = "";
        $strHeaderRowsBot = "";
        foreach($this->arrDifHeader as $intIndiceDifHeader => $arrInfoHeader) {
          if(count($arrInfoHeader) > 1) {
            $strHeaderRowsTop .= "<th colspan='" . (count($arrInfoHeader) - 1) . "'";
            $strHeaderRowsTop .= " " . $this->GetHeaderStyle($intIndiceDifHeader);
            $strHeaderRowsTop .= ">{$arrInfoHeader[0]}</th>";
            foreach($arrInfoHeader as $strNombreCol) {
              if($strNombreCol == $arrInfoHeader[0]) continue;
              $arrStyles[$strNombreCol] = $this->GetHeaderStyle($intIndiceDifHeader);
              $strHeaderRowsBot .= "<th {$arrStyles[$strNombreCol]}>{$this->arrInfoReporte[$strNombreCol]["Nombre"]}</th>";
            }
          } else {
            $arrStyles[$arrInfoHeader[0]] = $this->GetHeaderStyle($intIndiceDifHeader);
            $strHeaderRowsTop .= "<th {$arrStyles[$arrInfoHeader[0]]} rowspan='2'>{$this->arrInfoReporte[$arrInfoHeader[0]]["Nombre"]}</th>";
          }
        }

        $strTabla .= "<tr>$strHeaderRowsTop</tr>";
        $strTabla .= "<tr>$strHeaderRowsBot</tr>";
      }
    }

    // Crear los encabezados
    if(!$bolHeaderCreado) {
      $strTabla .= "<tr>";
      foreach($this->arrInfoReporte as $strNombreCortoColumna => $arrInfoColumna) {
        $strTabla .= "<th>{$arrInfoColumna["Nombre"]}</th>";
      }
      $strTabla .= "</tr>";
    }

    // Llenar el cuerpo
    if(is_array($arrReporte)) {
      foreach($arrReporte as $arrInfoCRep) {
        $strClaseTD = 'td';
        if(isset($arrInfoCRep["xSubTotx"])) {
          $strClaseTD = 'th';
        } else if(isset($arrInfoCRep["xTotx"])) {
          $strClaseTD = 'th';
        }

        if($this->bolAlternarConClase) {
          $strTabla .= "<tr " . Variables::AlternarConClase() . ">";
        } else {
          $strTabla .= "<tr>";
        }
        $intSalto = 0;
        foreach($this->arrInfoReporte as $strNombreCortoColumna => $arrInfoColumna) {
          if(($arrInfoColumna["Tipo"]{0} == 'S') || ($arrInfoColumna["Tipo"]{0} == 'C')) {
            if(($arrInfoColumna["Tipo"]{0} == 'C') && (strlen($arrInfoColumna["Tipo"]) > 1) && ($strClaseTD != "td")) {
              $intSalto = substr($arrInfoColumna["Tipo"], 1);
              $strClaseTD2 = "th colspan='$intSalto'";
              $intSalto--;
            } else if($intSalto > 0) {
              $intSalto--;
              continue;
            } else {
              $strClaseTD2 = $strClaseTD;
            }
            if(!isset($arrInfoCRep[$strNombreCortoColumna])) {
              $arrInfoCRep[$strNombreCortoColumna] = "";
            }
            $strTabla .= "<$strClaseTD2>{$arrInfoCRep[$strNombreCortoColumna]}</$strClaseTD>";
          } else if($arrInfoColumna["Tipo"]{0} == 'T') {
            $strTabla .= "<$strClaseTD>" . CambioFechasRep($arrInfoCRep[$strNombreCortoColumna]) . "</$strClaseTD>";
          } else {
            $strTabla .= "<$strClaseTD class='AlinearDerecha'>";
            if((@strlen($arrInfoColumna["Tipo"]) > 1) && (@$arrInfoColumna["Tipo"]{1} == 'x') && ((@$arrInfoCRep[$strNombreCortoColumna] - 0) == 0)) {
              $strTabla .= "--";
            } else {
              if($arrInfoColumna["Tipo"]{0} == 'I') {
                if($strClaseTD != 'td') {
                  if((strlen($arrInfoColumna["Tipo"]) > 1) && ($arrInfoColumna["Tipo"]{1} == 'x')) {
                    $strTabla .= ($arrInfoCRep[$strNombreCortoColumna] - 0);
                  } else {
                    $strTabla .= "&nbsp;";
                  }
                } else {
                  $strTabla .= (@$arrInfoCRep[$strNombreCortoColumna] - 0);
                }
              } else if($arrInfoColumna["Tipo"]{0} == 'D') {
                if($strClaseTD == "th") {
                  $strTabla .= "&nbsp;";
                } else {
                  $strTabla .= number_format(@$arrInfoCRep[$strNombreCortoColumna] - 0, 2);
                }
              } else if($arrInfoColumna["Tipo"]{0} == 'F') {
                if($strClaseTD == "th") {
                  $strTabla .= "&nbsp;";
                } else {
                  $strTabla .= '$' . number_format($arrInfoCRep[$strNombreCortoColumna] - 0, 2);
                }
              }
            }
            $strTabla .= "</$strClaseTD>";
          }
        }
        $strTabla .= "</tr>";
      }
    }
    
    $strTabla .= $this->IncluirHTMLDespues();
    
    $strTabla .= "</table>";

    return $strTabla;
  }

  function __ObtenerEstiloCabeceraPear($intIndex) {
    static $arrIndiceColores = "";
    static $intIndiceColores = "";
    static $arrEstilos       = "";

    if($this->rsEstiloDefault == "AAA") {
      $this->rsEstiloDefault =& $this->rsWorkBook->addFormat();
      $this->rsEstiloDefault->setAlign     ('top');
      $this->rsEstiloDefault->setAlign     ('center');
      $this->rsEstiloDefault->setTextWrap  ();
      $this->rsEstiloDefault->setBold      ();
      $this->rsEstiloDefault->setFgColor   (11);
      $this->rsEstiloDefault->setColor     ('black');
      $this->rsEstiloDefault->setFontFamily('Arial');
      $this->rsEstiloDefault->setSize      ('10');

      $intIndiceColores = 0;
      $arrIndiceColores = array();
      $arrEstilos       = array();

      $arrIndiceColores['black'] = 'black';
    }

    if(!isset($this->arrDifHeaderCol[$intIndex])) {
      return $this->rsEstiloDefault;
    }

    if(is_array($this->arrDifHeaderCol[$intIndex])) {
      if(isset($arrEstilos[$intIndex])) {
        return $arrEstilos[$intIndex];
      }
      if(count($this->arrDifHeaderCol[$intIndex]) == 3) {
        $strHexColBg = Reporte::GetHexColor($this->arrDifHeaderCol[$intIndex][0], $this->arrDifHeaderCol[$intIndex][1], $this->arrDifHeaderCol[$intIndex][2]);
        $strHexColFg = 'black';
      } else if(count($this->arrDifHeaderCol[$intIndex]) == 6) {
        $strHexColBg = Reporte::GetHexColor($this->arrDifHeaderCol[$intIndex][0], $this->arrDifHeaderCol[$intIndex][1], $this->arrDifHeaderCol[$intIndex][2]);
        $strHexColFg = Reporte::GetHexColor($this->arrDifHeaderCol[$intIndex][3], $this->arrDifHeaderCol[$intIndex][4], $this->arrDifHeaderCol[$intIndex][5]);
      } else {
        return $this->rsEstiloDefault;
      }

      if(!$arrIndiceColores[$strHexColBg]) {
        $this->rsWorkBook->setCustomColor($intIndiceColores + 20, $this->arrDifHeaderCol[$intIndex][0], $this->arrDifHeaderCol[$intIndex][1], $this->arrDifHeaderCol[$intIndex][2]);
        $arrIndiceColores[$strHexColBg] = $intIndiceColores + 20;
        $intIndiceColores++;
      }
      if(!$arrIndiceColores[$strHexColFg]) {
        $this->rsWorkBook->setCustomColor($intIndiceColores + 20, $this->arrDifHeaderCol[$intIndex][3], $this->arrDifHeaderCol[$intIndex][4], $this->arrDifHeaderCol[$intIndex][5]);
        $arrIndiceColores[$strHexColFg] = $intIndiceColores + 20;
        $intIndiceColores++;
      }

      $arrEstilos[$intIndex] =& $this->rsWorkBook->addFormat();
      $arrEstilos[$intIndex]->setAlign     ('top');
      $arrEstilos[$intIndex]->setAlign     ('center');
      $arrEstilos[$intIndex]->setTextWrap  ();
      $arrEstilos[$intIndex]->setBold      ();
      $arrEstilos[$intIndex]->setFgColor   ($arrIndiceColores[$strHexColBg]);
      $arrEstilos[$intIndex]->setColor     ($arrIndiceColores[$strHexColFg]);
      $arrEstilos[$intIndex]->setFontFamily('Arial');
      $arrEstilos[$intIndex]->setSize      ('10');

      return $arrEstilos[$intIndex];
    } else {
      if($this->arrDifHeaderCol[$intIndex] == $intIndex) {
        return $this->rsEstiloDefault;
      } else {
        return $this->__ObtenerEstiloCabeceraPear($this->arrDifHeaderCol[$intIndex]);
      }
    }
  }

  function __DoReportePear($arrReporte) {
    $intOffsetY = 0;
    $intOffsetX = 0;

//    Variables::DebugBacktrace("Antes", false);
//    error_reporting(E_ALL);
    $this->rsWorkBook = new Spreadsheet_Excel_Writer();
//    Variables::DebugBacktrace("Despues", false);
    $this->rsWorkBook->send('test' . date('YmdHis') . '.xls');

    /*
    $rsWorkBook->setCustomColor(11,  78, 154,   6); // HEADER
    $rsWorkBook->setCustomColor(12, 201, 238, 150); // SUBTOTAL
    $rsWorkBook->setCustomColor(13,   0, 102,   0); // TOTAL
    // */
    /*
    $rsWorkBook->setCustomColor(11,  80,  86, 154); // HEADER
    $rsWorkBook->setCustomColor(12, 121, 150, 238); // SUBTOTAL
    $rsWorkBook->setCustomColor(13,   0,   0, 102); // TOTAL
    // */
    //*
    $this->rsWorkBook->setCustomColor(11, 120, 120, 120); // HEADER
    $this->rsWorkBook->setCustomColor(12, 200, 200, 200); // SUBTOTAL
    $this->rsWorkBook->setCustomColor(13,   0,   0,   0); // TOTAL
    // */

    $rsFormatHeader =& $this->rsWorkBook->addFormat();
    $rsFormatSubTot =& $this->rsWorkBook->addFormat();
    $rsFormatTot    =& $this->rsWorkBook->addFormat();
    $rsFormatNormal =& $this->rsWorkBook->addFormat();

    $rsFormatNormal->setFgColor   ('white');
    $rsFormatNormal->setColor     ('black');
    $rsFormatNormal->setFontFamily('Arial');
    $rsFormatNormal->setSize      ('8');

    $rsFormatSubTot->setBold      ();
    $rsFormatSubTot->setFgColor   (12);
    $rsFormatSubTot->setColor     ('black');
    $rsFormatSubTot->setFontFamily('Arial');
    $rsFormatSubTot->setSize      ('10');

    $rsFormatTot   ->setBold      ();
    $rsFormatTot   ->setFgColor   (13);
    $rsFormatTot   ->setColor     ('white');
    $rsFormatTot   ->setFontFamily('Arial');
    $rsFormatTot   ->setSize      ('10');

    $rsWorkSheet =& $this->rsWorkBook->addWorkSheet('Reporte');

    $rsWorkSheet->write   ($intOffsetY, $intOffsetX, $this->__CambiarSimbolos($this->strNombreReporte), $rsFormatTot);
    $rsWorkSheet->setMerge($intOffsetY, $intOffsetX, $intOffsetY, $intOffsetX + count($this->arrInfoReporte) - 1);
    $intOffsetY++;

    // Si se pasa el arreglo de arrDifHeader, y este contiene algun elemento cuyo
    // count sea mayor a 1, el header es distinto
    $bolHeaderCreado = false;
    if($this->arrDifHeader != "") {
      $bolColumnasAgrupadas = false;
      foreach($this->arrDifHeader as $arrInfoHeader) {
        if(count($arrInfoHeader) > 1) {
          $bolColumnasAgrupadas = true;
          break;
        }
      }

      if($bolColumnasAgrupadas) {
        $bolHeaderCreado = true;
        $x = 0;
        foreach($this->arrDifHeader as $intIndiceDifHeader => $arrInfoHeader) {
          if(count($arrInfoHeader) > 1) {
            $x2 = 0;
            foreach($arrInfoHeader as $strNombreCol) {
              if($strNombreCol == $arrInfoHeader[0]) continue;
              $rsWorkSheet->write($intOffsetY + 1, $intOffsetX + $x + $x2, $this->__CambiarSimbolos($this->arrInfoReporte[$strNombreCol]["Nombre"]), $this->__ObtenerEstiloCabeceraPear($intIndiceDifHeader));
//              $rsWorkSheet->write($intOffsetY + 1, $intOffsetX + $x + $x2, $this->__CambiarSimbolos($this->arrInfoReporte[$strNombreCol]["Nombre"]), $rsFormatHeader);
              $x2++;
            }

            $rsWorkSheet->write   ($intOffsetY, $intOffsetX + $x, $this->__CambiarSimbolos($arrInfoHeader[0]), $this->__ObtenerEstiloCabeceraPear($intIndiceDifHeader));
//            $rsWorkSheet->write   ($intOffsetY, $intOffsetX + $x, $this->__CambiarSimbolos($arrInfoHeader[0]), $rsFormatHeader);
            $arrMerges[] = array($intOffsetX + $x, $intOffsetX + $x + $x2 - 1);
//            $rsWorkSheet->setMerge($intOffsetY, $intOffsetX + $x, $intOffsetY, $intOffsetX + $x + $x2);

            $x += $x2;
          } else {
            $rsWorkSheet->write   ($intOffsetY, $intOffsetX + $x, $this->__CambiarSimbolos($this->arrInfoReporte[$arrInfoHeader[0]]["Nombre"]), $this->__ObtenerEstiloCabeceraPear($intIndiceDifHeader));
//            $rsWorkSheet->write   ($intOffsetY, $intOffsetX + $x, $this->__CambiarSimbolos($this->arrInfoReporte[$arrInfoHeader[0]]["Nombre"]), $rsFormatHeader);
            $rsWorkSheet->setMerge($intOffsetY, $intOffsetX + $x, $intOffsetY + 1, $intOffsetX + $x);
            $x++;
          }
        }

        $arrMerges = array_reverse($arrMerges);

        foreach($arrMerges as $arrPos) {
          $rsWorkSheet->setMerge($intOffsetY, $arrPos[0], $intOffsetY, $arrPos[1]);
        }

        $intOffsetY++;
      }
    }

    if(!$bolHeaderCreado) {
      // Cabecera
      $x = 0;
      foreach($this->arrInfoReporte as $strNombreCortoColumna => $arrInfoColumna) {
        $rsWorkSheet->write($intOffsetY, $intOffsetX + $x, $this->__CambiarSimbolos($arrInfoColumna["Nombre"]), $rsFormatHeader);
        $x++;
      }
    }

    // Llenar el cuerpo
    $y = 0;
    if(is_array($arrReporte)) {
      foreach($arrReporte as $arrInfoCRep) {
        $y++;
        $rsFormat = $rsFormatNormal;

        if(isset($arrInfoCRep["xSubTotx"])) {
          $rsFormat = $rsFormatSubTot;
        } else if(isset($arrInfoCRep["xTotx"])) {
          $rsFormat = $rsFormatTot;
        }

        $x = 0;
        foreach($this->arrInfoReporte as $strNombreCortoColumna => $arrInfoColumna) {
          if(($arrInfoColumna["Tipo"]{0} == 'S') || ($arrInfoColumna["Tipo"]{0} == 'C')) {
            // Cadena normal
            $rsWorkSheet->write($intOffsetY + $y, $intOffsetX + $x, $this->__CambiarSimbolos($arrInfoCRep[$strNombreCortoColumna]), $rsFormat);
          } else if($arrInfoColumna["Tipo"]{0} == 'T') {
            // Fecha
            $rsWorkSheet->write($intOffsetY + $y, $intOffsetX + $x, $arrInfoCRep[$strNombreCortoColumna], $rsFormat);
          } else {
            // Alinear a la derecha
            if((strlen($arrInfoColumna["Tipo"]) > 1) && ($arrInfoColumna["Tipo"]{1} == 'x') && (($arrInfoCRep[$strNombreCortoColumna] - 0) == 0)) {
              // Va vacio, con --
              $rsWorkSheet->write($intOffsetY + $y, $intOffsetX + $x, 0, $rsFormat);
            } else {
              if($arrInfoColumna["Tipo"]{0} == 'I') {
                // Numero entero normal
                $rsWorkSheet->write($intOffsetY + $y, $intOffsetX + $x, round($arrInfoCRep[$strNombreCortoColumna], 0), $rsFormat);
              } else if($arrInfoColumna["Tipo"]{0} == 'D') {
                // Decimal normal
                if(isset($arrInfoCRep["xSubTotx"]) || isset($arrInfoCRep["xTotx"])) {
                  $rsWorkSheet->write($intOffsetY + $y, $intOffsetX + $x, "", $rsFormat);
                } else {
                  $rsWorkSheet->write($intOffsetY + $y, $intOffsetX + $x, round($arrInfoCRep[$strNombreCortoColumna], 2), $rsFormat);
                }
              } else if($arrInfoColumna["Tipo"]{0} == 'F') {
                // Currency
                if(isset($arrInfoCRep["xSubTotx"]) || isset($arrInfoCRep["xTotx"])) {
                  $rsWorkSheet->write($intOffsetY + $y, $intOffsetX + $x, "", $rsFormat);
                } else {
                  $rsWorkSheet->write($intOffsetY + $y, $intOffsetX + $x, round($arrInfoCRep[$strNombreCortoColumna], 2), $rsFormat);
                }
              }
            }
          }
          $x++;
        }
      }
    }

    $this->rsWorkBook->close();
    die;
  }

}
?>